﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using Flagwind.Collections;

namespace Flagwind.Data
{
	/// <summary>
	/// 表示数据访问的泛型类。
	/// </summary>
	public class DataAccess<T> : IDataAccess where T : class, IEntity, new()
	{
		#region 私有字段

		private DataAccess _dataAccess;
		private static DataAccess<T> _instance;
		private IDataProvider _dataProvider;

		#endregion

		#region 保护属性

		protected IDataProvider ExecuteProvider
		{
			get
			{
				if(_dataProvider == null)
				{
					return DataProviderFactory.GetProvider();
				}
				else
				{
					return _dataProvider;
				}
			}
		}

		#endregion

		#region 公共属性

		public IDataProvider DefaultProvider
		{
			get
			{
				if(_dataProvider == null)
				{
					return DataProviderFactory.DefaultProvider;
				}
				else
				{
					return _dataProvider;
				}
			}
			set
			{
				_dataProvider = value;
			}
		}

		public NamedCollection<IDataProvider> DataProviders
		{
			get
			{
				return _dataAccess.DataProviders;
			}
		}

		#endregion

		#region 静态属性

		public static DataAccess<T> Instance
		{
			get
			{
				if(_instance == null)
				{
					_instance = new DataAccess<T>();
				}
				return _instance;
			}
		}

		#endregion

		#region 构造方法

		protected internal DataAccess()
		{
			this._dataAccess = new DataAccess();
		}

		public DataAccess(IDataProvider dataProvider)
		{
			this.DefaultProvider = dataProvider;
			this._dataAccess = new DataAccess(dataProvider);
		}

		#endregion

		#region 公共方法

		#region 新增方法

		/// <summary>
		/// 新增一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <returns>受影响的记录数。</returns>
		public int Insert(T entity)
		{
			return this.Insert(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，新增一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Insert(T entity, IDataProvider provider)
		{
			return _dataAccess.Insert(entity, provider);
		}

		/// <summary>
		/// 批量新增一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <returns>受影响的记录数。</returns>
		public int Insert(IEnumerable<T> entities)
		{
			return this.Insert(entities, this.ExecuteProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，批量新增一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Insert(IEnumerable<T> entities, IDataProvider provider)
		{
			return _dataAccess.Insert(entities, provider);
		}

		#endregion

		#region 更新方法

		/// <summary>
		/// 更新一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <returns>受影响的记录数。</returns>
		public int Update(T entity)
		{
			return this.Update(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，更新一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Update(T entity, IDataProvider provider)
		{
			return _dataAccess.Update(entity, provider);
		}

		/// <summary>
		/// 批量更新一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <returns>受影响的记录数。</returns>
		public int Update(IEnumerable<T> entities)
		{
			return this.Update(entities, this.ExecuteProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，批量更新一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Update(IEnumerable<T> entities, IDataProvider provider)
		{
			return _dataAccess.Update(entities, provider);
		}

		#endregion

		#region 删除方法

		/// <summary>
		/// 删除一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <returns>受影响的记录数。</returns>
		public int Delete(T entity)
		{
			return this.Delete(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，删除一个实体类数据。
		/// </summary>
		/// <param name="entity">实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Delete(T entity, IDataProvider provider)
		{
			return _dataAccess.Delete(entity, provider);
		}

		/// <summary>
		/// 批量删除一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <returns>受影响的记录数。</returns>
		public int Delete(IEnumerable<T> entities)
		{
			return this.Delete(entities, this.ExecuteProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，批量删除一组实体类数据。注：内部使用事物。
		/// </summary>
		/// <param name="entities">实体对象集合。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Delete(IEnumerable<T> entities, IDataProvider provider)
		{
			return _dataAccess.Update(entities, provider);
		}

		#endregion

		#region 查询方法

		/// <summary>
		/// 根据实体查询表达式，查询实体对象集合。
		/// </summary>
		/// <typeparam name="T">实体类型。</typeparam>
		/// <param name="command">实体查询表达式。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(Command command)
		{
			return this.Select(command, this.DefaultProvider);
		}

		/// <summary>
		/// 根据实体查询表达式及数据访问提供程序，查询实体对象集合。 
		/// </summary>
		/// <typeparam name="T">实体类型。</typeparam>
		/// <param name="command">实体查询表示。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(Command command, IDataProvider provider)
		{
			// 如果开启了分页且记录总数为0，直接返回空集合
			if(command.PageEnable)
			{
				if(command.RecordCount == 0)
				{
					if(!command.haveOrderBy)
					{
						throw new Exception("如果要进行分页并且同时获得记录总数，Command必须有排序操作，请调用OrderBy方法。");
					}
					T entity = this.Find(command, provider);
					command.RecordCount = Utility.ConvertType<int>(entity.GetPropertyValues()[0]);
					//如果记录总数仍然是0，直接返回空集合。
					if(command.RecordCount == 0)
					{
						return new List<T>();
					}
				}
			}
			IDataReader reader = AnonymousAccess.GetReader(command, typeof(T), provider);
			return DataUtility.ResolveEntityList<T>(reader, command.GetEntityTableName());
		}

		/// <summary>
		/// 根据指定的查询表达式，并附加当前类型的实体类中改变了值的属性作为查询条件。
		/// </summary>
		/// <param name="command">查询表达式。</param>
		/// <param name="entity">实体对象。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(Command command, T entity)
		{
			return this.Select(command, entity, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定的查询表达式和数据访问提供程序，并附加当前类型的实体类中改变了值的属性作为查询条件。
		/// </summary>
		/// <param name="command">查询表达式。</param>
		/// <param name="entity">实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(Command command, T entity, IDataProvider provider)
		{
			string sql = string.Empty;
			if(entity.GetPropertyChangedList().Count > 0)
			{
				IDataParameter[] parameters;
				sql = DataUtility.FillParameter(command, entity, provider, out parameters);
				IDataReader reader = DefaultProvider.ExecuteDataReader(sql, CommandType.Text, parameters);
				return DataUtility.ResolveEntityList<T>(reader, command.GetEntityTableName());
			}
			else
			{
				return this.Select(command);
			}
		}

		/// <summary>
		/// 根据指定的条件，从数据阅读器中筛选出需要的实体对象。本方法适用于数据量不是很大的数据库表，要加强效率，请使用Command表达式。
		/// </summary>
		/// <param name="condition">条件实例。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(ConditionHandle<T> condition)
		{
			return this.Select(condition, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定的条件，及数据访问提供程序，从数据阅读器中筛选出需要的实体对象。本方法适用于数据量不是很大的数据库表，要加强效率，请使用Command表达式。
		/// </summary>
		/// <param name="condition">条件实例。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(ConditionHandle<T> condition, IDataProvider provider)
		{
			T entity = new T();
			string sql = "SELECT " + string.Join(",", Utility.PrepareSqlFields(entity.GetPropertyNames())) + " FROM " + entity.GetSchemeTableName();
			IDataReader reader = provider.ExecuteDataReader(sql);
			List<T> list = new List<T>();
			using(reader)
			{
				int fieldCount = entity.GetPropertyNames().Length;
				while(reader.Read())
				{
					object[] values = new object[fieldCount];
					reader.GetValues(values);
					T t = new T();
					t.SetPropertyNames(entity.GetPropertyNames());
					t.SetPropertyValues(values);
					//根据条件过滤
					if(condition(t))
					{
						list.Add(t);
					}
				}
			}
			return list;
		}

		/// <summary>
		/// 缓存 Command 的结果然后从数据库查询数据，使得不必每次处理 Command 对象。
		/// </summary>
		/// <param name="key">缓存的KEY。</param>
		/// <param name="func">如果没有缓存的项，要初始化缓存项的委托函数。</param>
		/// <param name="action">要处理的查询的委托。</param>
		/// <param name="parameter">相关的初始化操作的参数对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> Select(string key, CacheFunc func, SqlInfoAction action, object parameter, IDataProvider provider)
		{
			var info = SqlInfoManager.Get(key);
			if(info == null)
			{
				var Command = func(parameter);
				info = DataUtility.GetSqlInfo(Command, typeof(T), provider, false);
				SqlInfoManager.Add(key, info);
			}
			IDataReader reader = AnonymousAccess.GetReader(info, provider, false);
			return DataUtility.ResolveEntityList<T>(reader, info.TableName);
		}

		/// <summary>
		/// 查询实体类集合，并同时查询关联的所有的子实体类集合。
		/// </summary>
		/// <param name="command">查询表达式。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象集合。</returns>
		public IEnumerable<T> SelectWithChild(Command command, IDataProvider provider)
		{
			IEnumerable<T> result = this.Select(command, provider);
			if(result.Any())
			{
				DataAccess<T> dataAccess = new DataAccess<T>();
				//下面采取缓存方式获取属性名字数组
				var cache = MemoryCache<object>.Default;
				string key = "CELPN_" + typeof(T).FullName;
				object cacheValue = cache.Get<DataAccess<T>>(key, p => p.GetAllChildEntityListPropertyName(), dataAccess);
				var propNames = (string[])cacheValue;
				dataAccess.SelectChildResult(propNames, result, provider);
			}
			return result;
		}

		/// <summary>
		/// 根据指定查询表达式，查询一个实体对象。
		/// </summary>
		/// <typeparam name="T">实体类型。</typeparam>
		/// <param name="command">查询表达式。</param>
		/// <returns>实体对象。</returns>
		public T Find(Command command)
		{
			return this.Find(command, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定查询表达式及数据访问提供程序，查询一个实体对象。
		/// </summary>
		/// <typeparam name="T">实体类型。</typeparam>
		/// <param name="command">查询表达式。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>实体对象。</returns>
		public T Find(Command command, IDataProvider provider)
		{
			var reader = AnonymousAccess.GetReader(command, typeof(T), provider, true);
			return DataUtility.ResolveEntity<T>(reader);
		}

		/// <summary>
		/// 填充实体对象，注：必须有主键值才可以填充成功。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <returns>填充后的实体对象。</returns>
		public bool Fill(T entity)
		{
			return this.Fill(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定数据访问提供程序填充实体对象，注：必须有主键值才可以填充成功。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>填充后的实体对象。</returns>
		public bool Fill(T entity, IDataProvider provider)
		{
			return _dataAccess.Fill(entity, provider);
		}

		/// <summary>
		/// 检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(IEntity entity)
		{
			return this.Exists(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定的数据访问提供程序，检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(IEntity entity, IDataProvider provider)
		{
			return _dataAccess.Exists(entity, provider);
		}

		/// <summary>
		/// 检测查询表达式是否在数据存中存在。
		/// </summary>
		/// <param name="command">查询表达式。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(Command command)
		{
			return this.Exists(command, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定的数据访问提供程序，检测查询表达式是否在数据库中存在。
		/// </summary>
		/// <param name="command">查询表达式。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(Command command, IDataProvider provider)
		{
			return _dataAccess.Exists(command, provider);
		}

		#endregion

		#region 执行方法

		/// <summary>
		/// 执行一个不返回结果集的 Command 表达式，例如更新，删除实体类的操作。
		/// </summary>
		/// <param name="command">Command 表达式。</param>
		/// <returns>受影响的记录数。</returns>
		public int Execute(Command command)
		{
			return this.Execute(command, this.DefaultProvider);
		}

		/// <summary>
		/// 使用指定的数据访问提供程序，执行一个不返回结果集的 Command 表达式，例如更新，删除实体类的操作。
		/// </summary>
		/// <param name="command">Command 表达式。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>受影响的记录数。</returns>
		public int Execute(Command command, IDataProvider provider)
		{
			return _dataAccess.Execute(command, provider);
		}

		#endregion

		#endregion

		#region IDataAccess 成员

		#region 新增方法

		int IDataAccess.Insert(IEntity entity)
		{
			return _dataAccess.Insert(entity);
		}

		int IDataAccess.Insert(IEntity entity, IDataProvider provider)
		{
			return _dataAccess.Insert(entity, provider);
		}

		int IDataAccess.Insert(IEnumerable<IEntity> entities)
		{
			return _dataAccess.Insert(entities);
		}

		int IDataAccess.Insert(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			return _dataAccess.Insert(entities, provider);
		}

		#endregion

		#region 修改方法

		int IDataAccess.Update(IEntity entity)
		{
			return this._dataAccess.Update(entity);
		}

		int IDataAccess.Update(IEntity entity, IDataProvider provider)
		{
			return this._dataAccess.Update(entity, provider);
		}

		int IDataAccess.Update(IEnumerable<IEntity> entities)
		{
			return this._dataAccess.Update(entities);
		}

		int IDataAccess.Update(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			return this._dataAccess.Update(entities, provider);
		}

		#endregion

		#region 删除方法

		int IDataAccess.Delete(IEntity entity)
		{
			return this._dataAccess.Delete(entity);
		}

		int IDataAccess.Delete(IEntity entity, IDataProvider provider)
		{
			return this._dataAccess.Delete(entity, provider);
		}

		int IDataAccess.Delete(IEnumerable<IEntity> entities)
		{
			return this._dataAccess.Delete(entities);
		}

		int IDataAccess.Delete(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			return this._dataAccess.Delete(entities, provider);
		}

		#endregion

		#region 查询方法

		IEnumerable<T> IDataAccess.Select<T>(Command command)
		{
			return this._dataAccess.Select<T>(command);
		}

		IEnumerable<T> IDataAccess.Select<T>(Command command, IDataProvider provider)
		{
			return this._dataAccess.Select<T>(command, provider);
		}

		T IDataAccess.Find<T>(Command command)
		{
			return this._dataAccess.Find<T>(command);
		}

		T IDataAccess.Find<T>(Command command, IDataProvider provider)
		{
			return this._dataAccess.Find<T>(command, provider);
		}

		bool IDataAccess.Fill(IEntity entity)
		{
			return this._dataAccess.Fill(entity);
		}

		bool IDataAccess.Fill(IEntity entity, IDataProvider provider)
		{
			return this._dataAccess.Fill(entity, provider);
		}

		/// <summary>
		/// 检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		bool IDataAccess.Exists(IEntity entity)
		{
			return this._dataAccess.Exists(entity);
		}

		/// <summary>
		/// 根据指定的数据访问提供程序，检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		bool IDataAccess.Exists(IEntity entity, IDataProvider provider)
		{
			return this._dataAccess.Exists(entity, provider);
		}

		bool IDataAccess.Exists(Command command)
		{
			return this._dataAccess.Exists(command);
		}

		bool IDataAccess.Exists(Command command, IDataProvider provider)
		{
			return this._dataAccess.Exists(command, provider);
		}

		#endregion

		#region 执行方法

		int IDataAccess.Execute(Command command)
		{
			return this._dataAccess.Execute(command);
		}

		int IDataAccess.Execute(Command command, IDataProvider provider)
		{
			return this._dataAccess.Execute(command, provider);
		}

		#endregion

		#region 事务处理

		/// <summary>
		/// 打开连接并开启事务。
		/// </summary>
		public void BeginTransaction()
		{
			_dataAccess.BeginTransaction();
		}

		/// <summary>
		/// 开启事务并指定事务隔离级别。
		/// </summary>
		/// <param name="level">事务隔离级别。</param>
		public void BeginTransaction(IsolationLevel level)
		{
			_dataAccess.BeginTransaction(level);
		}

		/// <summary>
		/// 提交事务并关闭连接。
		/// </summary>
		public void Commit()
		{
			_dataAccess.Commit();
		}

		/// <summary>
		/// 回滚事务并关闭连接。
		/// </summary>
		public void Rollback()
		{
			_dataAccess.Rollback();
		}

		#endregion

		#endregion

		#region 私有方法

		private List<TChild> SelectChild<TChild>(IEnumerable<T> entitys, IDataProvider db) where TChild : class, IEntity, new()
		{
			TChild child = new TChild();
			var count = 0;
			var parameterNames = new List<string>();
			var parameters = new List<IDataParameter>();
			var sql = "SELECT * FROM " + child.GetMappingName() + " WHERE " + child.GetForeignKey<T>() + " IN ({0})";
			foreach(T e in entitys)
			{
				string name = db.ParameterChar + "P" + count;
				parameterNames.Add(name);
				parameters.Add(db.GetParameter(name, e[e.GetPrimaryKey()[0]]));
				count++;
			}
			//会有2100个参数的限制问题，下期解决
			string objSql = string.Format(sql, string.Join(",", parameterNames.ToArray()));
			IDataReader reader = db.ExecuteDataReader(objSql, CommandType.Text, parameters.ToArray());
			return DataUtility.ResolveEntityList<TChild>(reader, child.GetMappingName());
		}

		private void SelectChildResult(string[] childPropNames, IEnumerable<T> entitys, IDataProvider provider)
		{
			string keyTemp = "QCR_" + typeof(T).FullName + "_";
			foreach(string name in childPropNames)
			{
				var cache = MemoryCache<object>.Default;
				string key = keyTemp + name; //修正原来只能有一个子实体类集合的问题
				object cacheValue = cache.Get<string>(key, p => GetRealQueryAndSetChild(p), name);
				RuntimeHandle handle = (RuntimeHandle)cacheValue;
				MethodInfo realMethod = (MethodInfo)MethodInfo.GetMethodFromHandle(handle.MethodHandle, handle.TypeHandle);
				var func = Delegate.CreateDelegate(typeof(Action<IEnumerable<T>, IDataProvider>), this, realMethod) as Action<IEnumerable<T>, IDataProvider>;
				//等同于 realMethod.Invoke(this, new object[] { entitys,db });
				func(entitys, provider);
			}
		}

		private RuntimeHandle GetRealQueryAndSetChild(string propName)
		{
			Type thisType = GetType();
			Type propType = typeof(T).GetProperty(propName).PropertyType.GetGenericArguments()[0];
			var method = thisType.GetMethod("QueryAndSetChild", BindingFlags.Instance | BindingFlags.NonPublic);
			var realMethod = method.MakeGenericMethod(propType);
			RuntimeTypeHandle typeHandle = thisType.TypeHandle;
			return new RuntimeHandle(realMethod.MethodHandle, typeHandle);
		}

		private void QueryAndSetChild<TChild>(IEnumerable<T> entitys, IDataProvider provider) where TChild : class, IEntity, new()
		{
			var childList = SelectChild<TChild>(entitys, provider);
			string childName = GetChildEntityListPropertyName<TChild>();
			MemberAccessor drm = new MemberAccessor();
			foreach(T e in entitys)
			{
				List<TChild> newChilds = new List<TChild>();
				foreach(TChild child in childList)
				{
					//待优化
					if(child[child.GetForeignKey<T>()].Equals(e[e.GetPrimaryKey()[0]]))
					{
						newChilds.Add(child);
					}
				}
				//为当前实体类设置子实体类集合
				var accessor = drm.FindAccessor<T>(childName);
				accessor.SetValue(e, newChilds);
			}
		}

		private string GetChildEntityListPropertyName<TChild>() where TChild : class, IEntity, new()
		{
			var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
			foreach(var p in props)
			{
				if(p.PropertyType.IsGenericType)
				{
					Type t = p.PropertyType.GetGenericTypeDefinition();
					if(t == typeof(IEnumerable<>) || t.GetInterface("IEnumerable`1") != null)
					{
						//"IS IEnumerable<>"
						Type[] tArr = p.PropertyType.GetGenericArguments();
						if(tArr.Length == 1 && tArr[0] == typeof(TChild))
						{
							return p.Name;
						}
					}
				}
			}
			return null;
		}

		private string[] GetAllChildEntityListPropertyName()
		{
			List<string> result = new List<string>();
			var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
			foreach(var p in props)
			{
				if(p.PropertyType.IsGenericType)
				{
					if(p.PropertyType.GetGenericArguments()[0].BaseType == typeof(EntityBase))
					{
						Type t = p.PropertyType.GetGenericTypeDefinition();
						if(t == typeof(IEnumerable<>) || t.GetInterface("IEnumerable`1") != null)
						{
							//"IS IEnumerable<>"
							result.Add(p.Name);
						}
					}
				}
			}
			return result.ToArray();
		}

		#endregion
	}
}